{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.4.1\n",
      "0.16.1\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "import tensorflow_addons as tfa\n",
    "print(tf.__version__)\n",
    "print(tfa.__version__)\n",
    "\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras import layers, models\n",
    "from tensorflow.keras import backend as K\n",
    "\n",
    "from tensorflow.keras.preprocessing import sequence\n",
    "import numpy as np\n",
    "\n",
    "from sklearn.model_selection import train_test_split\n",
    "import matplotlib.pyplot as plt\n",
    "import gensim\n",
    "from gensim.models.keyedvectors import KeyedVectors\n",
    "\n",
    "plt.rcParams['font.sans-serif']=['simsun'] #用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus']=False #用来正常显示负号"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# w2v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "w2v_path = \"../../../TCM embedding/result/TCM_w2v.txt\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "initial_vocab=[]\n",
    "vocab_emb = {}\n",
    "cnt = 0\n",
    "for line in open(w2v_path,encoding='UTF-8'):\n",
    "    if cnt != 0:\n",
    "        char_ls = line.split()\n",
    "        \n",
    "        initial_vocab.append(char_ls[0])\n",
    "        vocab_emb[char_ls[0]] = char_ls[1:]\n",
    "    else:\n",
    "        cnt += 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "#读取所有数据生成字典\n",
    "def build_data(train_path,initial_vocab):\n",
    "    datas = []#【【句子】，【对应标签】】\n",
    "    #先加入特殊词\n",
    "    vocabs = {'UNK'}\n",
    "    #vocabs.add('PAD')\n",
    "    \n",
    "    data=[]\n",
    "    cate=[]\n",
    "    temp_data = []\n",
    "    temp_cate = []\n",
    "    for line in open(train_path,encoding='UTF-8'):\n",
    "        if line == '\\n' or line.split()[0] == \"。\":\n",
    "            if len(temp_data)!= 0:\n",
    "                data.append(temp_data)\n",
    "                cate.append(temp_cate)\n",
    "                temp_data = []\n",
    "                temp_cate = []\n",
    "        else:\n",
    "            line_ls = line.split()\n",
    "            char = line_ls[0].strip()\n",
    "            label = line_ls[1].strip()\n",
    "            temp_data.append(char)\n",
    "            temp_cate.append(label)\n",
    "            \n",
    "    for item in range(0,len(data)):\n",
    "        datas.append([data[item],cate[item]])\n",
    "        for i in data[item]:#数据中的词加入词库\n",
    "            vocabs.add(i)\n",
    "    for i in initial_vocab:#w2v中的词加入词库\n",
    "        vocabs.add(i)\n",
    "    \n",
    "    embeddings_matrix = np.zeros((len(vocabs), 200))# 初始化矩阵\n",
    "    \n",
    "    #索引和BIO标签对应\n",
    "    label=set()\n",
    "    for item in cate:\n",
    "        for i in item:\n",
    "            label.add(i)\n",
    "    idx2label = {idx: label for idx, label in enumerate(list(label))}\n",
    "    label2idx = {label: idx for idx,label  in idx2label.items()}\n",
    "    \n",
    "    #字符和索引编号对应\n",
    "    idx2vocab = {idx: char for idx, char in enumerate(list(vocabs))}\n",
    "    vocab2idx = {char: idx for idx, char in idx2vocab.items()}\n",
    "    \n",
    "    # 权重矩阵\n",
    "    for i in idx2vocab:\n",
    "        if idx2vocab[i] in initial_vocab:\n",
    "            embeddings_matrix[i] = vocab_emb[idx2vocab[i]]\n",
    "        \n",
    "    #self.write_file(list(vocabs), self.vocab_path)\n",
    "    return datas,label,idx2vocab,vocab2idx,idx2label,label2idx,embeddings_matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "datas,label,idx2vocab,vocab2idx,idx2label,label2idx,embeddings_matrix = build_data('../../data/LAB2/corpus.txt',initial_vocab)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "#读取所有数据生成字典\n",
    "def load_data(train_path):\n",
    "    datas = []#【【句子】，【对应标签】】\n",
    "\n",
    "    \n",
    "    data=[]\n",
    "    cate=[]\n",
    "    temp_data = []\n",
    "    temp_cate = []\n",
    "    for line in open(train_path,encoding='UTF-8'):\n",
    "        if line == '\\n' or line.split()[0] == \"。\":\n",
    "            if len(temp_data)!= 0:\n",
    "                data.append(temp_data)\n",
    "                cate.append(temp_cate)\n",
    "                temp_data = []\n",
    "                temp_cate = []\n",
    "        else:\n",
    "            line_ls = line.split()\n",
    "            char = line_ls[0].strip()\n",
    "            label = line_ls[1].strip()\n",
    "            temp_data.append(char)\n",
    "            temp_cate.append(label)\n",
    "            \n",
    "    for item in range(0,len(data)):\n",
    "        datas.append([data[item],cate[item]])\n",
    "    \n",
    "    return datas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "train = load_data('../../data/LAB2/train.txt')\n",
    "test = load_data('../../data/LAB2/test.txt')\n",
    "dev = load_data('../../data/LAB2/dev.txt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "train.extend(dev)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "#按照索引建立训练数据\n",
    "def read_corpus(datas,vocab2idx,label2idx):\n",
    "    corpus, labels = [],[]\n",
    "    for item in datas:\n",
    "        sent_=item[0]\n",
    "        tag_=item[1]\n",
    "        sent_ids = [vocab2idx[char] if char in vocab2idx else vocab2idx['<UNK>'] for char in sent_]\n",
    "        tag_ids = [label2idx[label] if label in label2idx else 0 for label in tag_]\n",
    "        corpus.append(sent_ids)\n",
    "        labels.append(tag_ids)\n",
    "    return corpus,labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train,y_train = read_corpus(train,vocab2idx,label2idx)\n",
    "X_test,y_test = read_corpus(test,vocab2idx,label2idx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "579"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(X_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "120"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(X_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "length = [len(i) for i in X_train]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([150., 199., 113.,  63.,  29.,  12.,   3.,   3.,   5.,   2.]),\n",
       " array([  1.,  11.,  21.,  31.,  41.,  51.,  61.,  71.,  81.,  91., 101.]),\n",
       " <BarContainer object of 10 artists>)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.hist(length)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_len = 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CRF(layers.Layer):\n",
    "    def __init__(self, label_size):\n",
    "        super(CRF, self).__init__()\n",
    "        #随机生成0，1范围内shape × shape的矩阵\n",
    "        self.trans_params = tf.Variable(\n",
    "            tf.random.uniform(shape=(label_size, label_size)), name=\"transition\")\n",
    "    #实现 Graph Execution\n",
    "    @tf.function\n",
    "    def call(self, inputs, labels, seq_lens):#使用crf来计算损失\n",
    "        #inputs: 一个形状为[batch_size, max_seq_len, num_tags] 的tensor,一般使用BILSTM处理之后输出转换为他要求的形状作为CRF层的输入\n",
    "        #labels：形状为[batch_size, max_seq_len]的矩阵，真实标签\n",
    "        #seq_lens：一个形状为 [batch_size] 的向量,表示每个序列的长度\n",
    "        #transition_params：转移矩阵\n",
    "        log_likelihood, self.trans_params = tfa.text.crf_log_likelihood(\n",
    "                                                inputs, labels, seq_lens,\n",
    "                                                transition_params=self.trans_params)\n",
    "        #损失\n",
    "        loss = tf.reduce_sum(-log_likelihood)\n",
    "        return loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"model\"\n",
      "__________________________________________________________________________________________________\n",
      "Layer (type)                    Output Shape         Param #     Connected to                     \n",
      "==================================================================================================\n",
      "input_ids (InputLayer)          [(None, 100)]        0                                            \n",
      "__________________________________________________________________________________________________\n",
      "embedding (Embedding)           (None, 100, 200)     12807600    input_ids[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "bidirectional (Bidirectional)   (None, 100, 400)     641600      embedding[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "dense (Dense)                   (None, 100, 16)      6416        bidirectional[0][0]              \n",
      "__________________________________________________________________________________________________\n",
      "target_ids (InputLayer)         [(None, 100)]        0                                            \n",
      "__________________________________________________________________________________________________\n",
      "input_lens (InputLayer)         [(None,)]            0                                            \n",
      "__________________________________________________________________________________________________\n",
      "crf (CRF)                       ()                   256         dense[0][0]                      \n",
      "                                                                 target_ids[0][0]                 \n",
      "                                                                 input_lens[0][0]                 \n",
      "==================================================================================================\n",
      "Total params: 13,455,872\n",
      "Trainable params: 13,455,872\n",
      "Non-trainable params: 0\n",
      "__________________________________________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "K.clear_session()\n",
    "\n",
    "EPOCHS = 16\n",
    "BATCH_SIZE = 64\n",
    "EMBED_DIM = 200#词向量的维度\n",
    "HIDDEN_SIZE = 200#隐层状态的维数\n",
    "MAX_LEN = 100\n",
    "VOCAB_SIZE = len(vocab2idx)\n",
    "CLASS_NUMS = len(label2idx)\n",
    "\n",
    "inputs = layers.Input(shape=(MAX_LEN,), name='input_ids', dtype='int32')\n",
    "targets = layers.Input(shape=(MAX_LEN,), name='target_ids', dtype='int32')\n",
    "seq_lens = layers.Input(shape=(), name='input_lens', dtype='int32')\n",
    "\n",
    "#嵌入层 词汇表的大小；词向量的维度\n",
    "x = layers.Embedding(input_dim=VOCAB_SIZE, output_dim=EMBED_DIM,weights = [embeddings_matrix])(inputs)\n",
    "x = layers.Bidirectional(layers.LSTM(HIDDEN_SIZE, return_sequences=True))(x)\n",
    "logits = layers.Dense(CLASS_NUMS)(x)#全连接层 CLASS_NUMS：输出的单元个数，output_shape = (batch_size ,units),输入的形状input_shape =(batch_size,input_dim)\n",
    "loss = CRF(label_size=CLASS_NUMS)(logits, targets, seq_lens)\n",
    "\n",
    "model = models.Model(inputs=[inputs, targets, seq_lens], outputs=loss)\n",
    "\n",
    "print(model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('Failed to import pydot. You must `pip install pydot` and install graphviz (https://graphviz.gitlab.io/download/), ', 'for `pydotprint` to work.')\n"
     ]
    }
   ],
   "source": [
    "from keras.utils import plot_model\n",
    "plot_model(model, to_file='model.png')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CustomLoss(keras.losses.Loss):\n",
    "    def call(self, y_true, y_pred):\n",
    "        loss, pred = y_pred\n",
    "        return loss\n",
    "\n",
    "# 自定义Loss\n",
    "#model.compile(loss=CustomLoss(), optimizer='adam')\n",
    "# 或者使用lambda表达式\n",
    "#配置训练方法时，告知训练时用的优化器、损失函数和准确率评测标准\n",
    "model.compile(loss = lambda y_true, y_pred: y_pred, optimizer='adam')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(579, 100) (579, 100)\n"
     ]
    }
   ],
   "source": [
    "#加载训练集 数据填充\n",
    "train_datas = sequence.pad_sequences(X_train, maxlen=MAX_LEN)\n",
    "train_labels = sequence.pad_sequences(y_train, maxlen=MAX_LEN)\n",
    "train_seq_lens = np.array([MAX_LEN] * len(train_labels))\n",
    "labels_ = np.ones(len(train_labels))\n",
    "# train_labels = keras.utils.to_categorical(train_labels, CLASS_NUMS)\n",
    "print(np.shape(train_datas), np.shape(train_labels))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/16\n",
      "9/9 [==============================] - 23s 2s/step - loss: 10621.0049 - val_loss: 2587.2969\n",
      "Epoch 2/16\n",
      "9/9 [==============================] - 9s 985ms/step - loss: 3211.8288 - val_loss: 2036.2828\n",
      "Epoch 3/16\n",
      "9/9 [==============================] - 8s 904ms/step - loss: 2499.4411 - val_loss: 1674.0071\n",
      "Epoch 4/16\n",
      "9/9 [==============================] - 8s 920ms/step - loss: 2117.2673 - val_loss: 1483.7163\n",
      "Epoch 5/16\n",
      "9/9 [==============================] - 8s 901ms/step - loss: 1980.0940 - val_loss: 1332.5874\n",
      "Epoch 6/16\n",
      "9/9 [==============================] - 7s 796ms/step - loss: 1669.7992 - val_loss: 1220.0721\n",
      "Epoch 7/16\n",
      "9/9 [==============================] - 7s 819ms/step - loss: 1559.6764 - val_loss: 1144.5988\n",
      "Epoch 8/16\n",
      "9/9 [==============================] - 7s 810ms/step - loss: 1438.0641 - val_loss: 1077.2483\n",
      "Epoch 9/16\n",
      "9/9 [==============================] - 7s 760ms/step - loss: 1313.0195 - val_loss: 1007.4601\n",
      "Epoch 10/16\n",
      "9/9 [==============================] - 8s 860ms/step - loss: 1176.7576 - val_loss: 958.3746\n",
      "Epoch 11/16\n",
      "9/9 [==============================] - 7s 829ms/step - loss: 1072.5160 - val_loss: 896.4401\n",
      "Epoch 12/16\n",
      "9/9 [==============================] - 8s 836ms/step - loss: 981.4306 - val_loss: 855.7252\n",
      "Epoch 13/16\n",
      "9/9 [==============================] - 7s 771ms/step - loss: 929.9456 - val_loss: 798.7759\n",
      "Epoch 14/16\n",
      "9/9 [==============================] - 8s 849ms/step - loss: 882.4697 - val_loss: 779.0287\n",
      "Epoch 15/16\n",
      "9/9 [==============================] - 8s 901ms/step - loss: 804.2549 - val_loss: 736.1425\n",
      "Epoch 16/16\n",
      "9/9 [==============================] - 7s 783ms/step - loss: 698.9738 - val_loss: 718.5543\n"
     ]
    }
   ],
   "source": [
    "#可以设定fit函数内validation_split参数为切分验证集的比例\n",
    "history = model.fit(x=[train_datas, train_labels, train_seq_lens], y=labels_, \n",
    "        validation_split = 0.1, batch_size=BATCH_SIZE, epochs=EPOCHS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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2jVIgjCRiQjt2rWrdW0tLjUJVV0B/MN7Os27HxdhuZjKjDFNjyWIva8u37GdcQ2wcCE9J0yAzZsTHYkxJgDCR31WVFTsfrxgwYLdj3fs2BG1b2HLli3MmzePFStWsGnTpr0SZzxYkgBYupR9X3gBAtzRZYzZuxYuXMgLL7wAwHXXXceOHTuYP38+ADfccAMAP/zwA/fcc8/uJPLhhx/uThqLF1dtDZ84cSLbtm2jbdu23HvvvYBLNkuXLqU6W7du5cEHqz1vOK4sSQDk5bn7RYsSG4cxJmH69+9PcnIy4JplWrRowcSJEwFo06YNAK+++ip/+9vf+PLLLxk6dCiXX345jz76KAUFBVxyySVs2eJG9m/YsIGpU6eSlJTEvHnz2LhxIx988AHvvPMOq1evrjaOuXPnBmpkVpOau6nOevZkV3IySZYkjGnWjj76aABSUtyusUWLFrtf+/HHH0lJSaFNmzY88MADfPzxx9x+++1cf/31ZGRkkJaWBrjawuuvv05WVhY333wzqsr333/PwoULWb58OdOnT682hhEjRjBz5sy99Alrz5IEQGoqOzt3puXXXyc6EmPMddeB18xTVy0qKsCrFQBw0EHwwANRl//3v//NnXfeyX777ccLL7ywO0n4O3q/++473nrrLd5++23uvfdeDj30UMrKynjllVcYM2YMF154IQDLly9n5MiRLFiwgCuvvJKKigoeeeQRrrnmGlasWEHXrl0pKyur1ed54okn6NWrF4sWLeKSSy4hPT2dyZMnk5mZycSJE7n77rvp2bNnxLL6siTh2b7vvrS0moQxzdIpp5zCKaecws033wzArl27gKpJYvjw4dx55520bNmSrl27smPHDlq3bs2mTZu49957eeihh5g0aRJ9+vQB4JtvvuGyyy7bXZNYsGABJSUlnHTSSVx//fUxxzZ9+nS6dOnC0UcfTW5uLk8//TRXXHEF77//PnfffTeHH344O3bsAIhYVl+WJDzbu3WD2bOhvBxS7GsxJmGqOeKP1Y56nKxWUVGxu2/CP8pp1qxZjB07lgsvvJCSkhLeeecdWrRowezZs6moqOCYY46psp2LL76Ys846i08++YRVq1Zx2mmnAbBo0aJa1STmz5/PmWeeCUCvXr146KGHALj66qu55ppryM3N5bbbbotaVl/Wce3Z3q0blJXBsmWJDsUYkyBb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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots()#创建一个figure \n",
    "ax.plot(history.history['loss'], '-r',label='训练集 loss')\n",
    "ax.plot(history.history['val_loss'], '-b',label='验证集 loss')\n",
    "\n",
    "####打开网格\n",
    "ax.grid(True)\n",
    "\n",
    "####定义x, y轴的名称\n",
    "ax.set_xlabel('epoch',fontsize=15)\n",
    "ax.set_ylabel('loss',fontsize=15)\n",
    "\n",
    "####定义标题\n",
    "fig.suptitle('实验二：BiLSTM-CRF模型损失函数变化曲线',fontsize=15)\n",
    "\n",
    "####展示图例 legend loc=是用来定义图例的位置的，还有很多选择，大家可以自己尝试\n",
    "ax.legend(loc = 'upper right')\n",
    "\n",
    "plt.savefig(\"实验二：BiLSTM-CRF模型损失函数变化曲线.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[7744.220703125,\n",
       " 3040.1123046875,\n",
       " 2478.486083984375,\n",
       " 2115.759521484375,\n",
       " 1871.599609375,\n",
       " 1666.96875,\n",
       " 1524.4407958984375,\n",
       " 1392.9923095703125,\n",
       " 1269.869384765625,\n",
       " 1176.9716796875,\n",
       " 1080.623291015625,\n",
       " 987.121337890625,\n",
       " 909.1254272460938,\n",
       " 833.5510864257812,\n",
       " 760.44580078125,\n",
       " 689.4114379882812]"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "history.history['loss']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2587.296875,\n",
       " 2036.2828369140625,\n",
       " 1674.007080078125,\n",
       " 1483.71630859375,\n",
       " 1332.58740234375,\n",
       " 1220.0721435546875,\n",
       " 1144.5987548828125,\n",
       " 1077.248291015625,\n",
       " 1007.4600830078125,\n",
       " 958.3746337890625,\n",
       " 896.4401245117188,\n",
       " 855.7252197265625,\n",
       " 798.7759399414062,\n",
       " 779.0287475585938,\n",
       " 736.1424560546875,\n",
       " 718.5543212890625]"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "history.history['val_loss']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:absl:Found untraced functions such as lstm_cell_1_layer_call_and_return_conditional_losses, lstm_cell_1_layer_call_fn, lstm_cell_2_layer_call_and_return_conditional_losses, lstm_cell_2_layer_call_fn, lstm_cell_1_layer_call_fn while saving (showing 5 of 10). These functions will not be directly callable after loading.\n",
      "WARNING:absl:Found untraced functions such as lstm_cell_1_layer_call_and_return_conditional_losses, lstm_cell_1_layer_call_fn, lstm_cell_2_layer_call_and_return_conditional_losses, lstm_cell_2_layer_call_fn, lstm_cell_1_layer_call_fn while saving (showing 5 of 10). These functions will not be directly callable after loading.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: output/BiLSTM_CRF_NER_MODEL\\assets\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: output/BiLSTM_CRF_NER_MODEL\\assets\n"
     ]
    }
   ],
   "source": [
    "# 保存\n",
    "model.save(\"output/BiLSTM_CRF_NER_MODEL\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = models.load_model(\"output/BiLSTM_CRF_NER_MODEL\", compile=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "with open('output/idx2vocab.json',\"w\",encoding=\"utf-8\") as f:\n",
    "    json.dump(idx2vocab,f)\n",
    "    \n",
    "with open('output/vocab2idx.json',\"w\",encoding=\"utf-8\") as f:\n",
    "    json.dump(vocab2idx,f)\n",
    "\n",
    "with open('output/idx2label.json',\"w\",encoding=\"utf-8\") as f:\n",
    "    json.dump(idx2label,f)\n",
    "\n",
    "with open('output/label2idx.json',\"w\",encoding=\"utf-8\") as f:\n",
    "    json.dump(label2idx,f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# model = models.load_model(\"output/bilstm_crf_ner\", compile=False)\n",
    "# 如果需要继续训练，需要下面的重新compile\n",
    "# model.compile(loss=lambda y_true, y_pred: y_pred, optimizer='adam')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "#提取转移矩阵参数\n",
    "trans_params = model.get_layer('crf').get_weights()[0]\n",
    "# 获得BiLSTM的输出logits\n",
    "sub_model = models.Model(inputs=model.get_layer('input_ids').input,\n",
    "                        outputs=model.get_layer('dense').output)\n",
    "\n",
    "def predict(model, inputs, input_lens):\n",
    "    logits = sub_model.predict(inputs)\n",
    "    # 获取CRF层的转移矩阵\n",
    "    # crf_decode：viterbi解码获得结果\n",
    "    pred_seq, viterbi_score = tfa.text.crf_decode(logits, trans_params, input_lens)\n",
    "    return pred_seq"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('output/idx2vocab.json',encoding=\"utf-8\") as file_obj:\n",
    "    idx2vocab=json.load(file_obj)\n",
    "    \n",
    "with open('output/vocab2idx.json',encoding=\"utf-8\") as file_obj:\n",
    "    vocab2idx=json.load(file_obj)\n",
    "    \n",
    "with open('output/idx2label.json',encoding=\"utf-8\") as file_obj:\n",
    "    idx2label=json.load(file_obj)\n",
    "    \n",
    "with open('output/label2idx.json',encoding=\"utf-8\") as file_obj:\n",
    "    label2idx=json.load(file_obj)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "temp_idx2vocab = {}\n",
    "for k,v in idx2vocab.items():\n",
    "    temp_idx2vocab[int(k)] = v\n",
    "idx2vocab = temp_idx2vocab.copy()\n",
    "\n",
    "temp_idx2label = {}\n",
    "for k,v in idx2label.items():\n",
    "    temp_idx2label[int(k)] = v\n",
    "idx2label = temp_idx2label.copy()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pre(X_test,idx2label,model,idx2vocab,vocab2idx):\n",
    "    result=[]\n",
    "    maxlen = MAX_LEN\n",
    "    for item in X_test:\n",
    "        sentence=''\n",
    "        for i in item:\n",
    "            sentence+=idx2vocab[i]\n",
    "        print(sentence)\n",
    "        sent_chars = list(sentence)\n",
    "        sent2id = [vocab2idx[word] if word in vocab2idx else vocab2idx['UNK'] for word in sent_chars]\n",
    "        sent2id_new = np.array([[0] * (maxlen-len(sent2id)) + sent2id[:maxlen]])\n",
    "        test_lens = np.array([MAX_LEN])\n",
    "        pred_seq = predict(model, sent2id_new, test_lens)\n",
    "        y_label = pred_seq.numpy().reshape(1, -1)[0]\n",
    "        y_ner = [idx2label[i] for i in y_label][-len(sent_chars):]\n",
    "        print(y_ner)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "#测试集效果检测\n",
    "test_text = []\n",
    "for item in X_test:\n",
    "    sen=''\n",
    "    for i in item:\n",
    "        sen+=idx2vocab[i]\n",
    "    test_text.append(sen)\n",
    "test_label=[]\n",
    "for item in y_test:\n",
    "    test_label.append([idx2label[i] for i in item])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "filename='test_text.json'\n",
    "with open(filename,'w',encoding='gbk') as file_obj:\n",
    "    json.dump(test_text,file_obj,ensure_ascii=False,indent = 4)\n",
    "    \n",
    "filename='test_label.json'\n",
    "with open(filename,'w',encoding='gbk') as file_obj:\n",
    "    json.dump(test_label,file_obj,ensure_ascii=False,indent = 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pre_data(sentence,vocab2idx,idx2label):\n",
    "    maxlen=MAX_LEN\n",
    "    sent_chars = list(sentence)\n",
    "    sent2id = [vocab2idx[word] if word in vocab2idx else vocab2idx['UNK'] for word in sent_chars]\n",
    "    sent2id_new = np.array([[0] * (maxlen-len(sent2id)) + sent2id[:maxlen]])\n",
    "    test_lens = np.array([MAX_LEN])\n",
    "\n",
    "    pred_seq = predict(model, sent2id_new, test_lens)\n",
    "    y_label = pred_seq.numpy().reshape(1, -1)[0]\n",
    "    y_ner = [idx2label[i] for i in y_label][-len(sent_chars):]\n",
    "    return y_ner"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "pre_result=[]\n",
    "count=0\n",
    "for sen in test_text:\n",
    "    #print(sen)\n",
    "    result=pre_data(sen,vocab2idx,idx2label)\n",
    "    #print(result)\n",
    "    pre_result.append(result)\n",
    "    count+=1\n",
    "    # print(count)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "filename='pre_result.json'\n",
    "with open(filename,'w',encoding='gbk') as file_obj:\n",
    "    json.dump(pre_result,file_obj,ensure_ascii=False,indent = 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "pre_ = []\n",
    "for s in pre_result:\n",
    "    for c in s:\n",
    "        pre_.append(c)\n",
    "        \n",
    "test_ = []\n",
    "for s in y_test:\n",
    "    for c in s:\n",
    "        test_.append(idx2label[c])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3060"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(pre_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3060"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(test_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "       B-DRU     0.8326    0.7863    0.8088       234\n",
      "      B-NAME     0.1000    0.2857    0.1481         7\n",
      "       B-QUA     0.9597    0.8720    0.9137       164\n",
      "       B-SYM     0.3364    0.5362    0.4134        69\n",
      "       B-USE     0.7513    0.8315    0.7893       178\n",
      "       E-DRU     0.7944    0.8333    0.8134       204\n",
      "      E-NAME     0.6500    0.8125    0.7222        16\n",
      "       E-QUA     0.8819    0.8247    0.8523       154\n",
      "       E-SYM     0.3028    0.5156    0.3815        64\n",
      "       E-USE     0.6338    0.7500    0.6870       120\n",
      "       I-DRU     0.4000    0.7368    0.5185        19\n",
      "      I-NAME     0.8824    0.4688    0.6122        96\n",
      "       I-QUA     0.0000    0.0000    0.0000         0\n",
      "       I-SYM     0.4571    0.4729    0.4649       203\n",
      "       I-USE     0.6698    0.8091    0.7329       351\n",
      "           O     0.8908    0.7460    0.8120      1181\n",
      "\n",
      "    accuracy                         0.7408      3060\n",
      "   macro avg     0.5964    0.6426    0.6044      3060\n",
      "weighted avg     0.7797    0.7408    0.7529      3060\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "E:\\download\\anaconda\\download\\lib\\site-packages\\sklearn\\metrics\\_classification.py:1221: UndefinedMetricWarning: Recall and F-score are ill-defined and being set to 0.0 in labels with no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, msg_start, len(result))\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import classification_report\n",
    "print(classification_report(pre_,test_,digits=4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYkAAAElCAYAAAARAx4oAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8vihELAAAACXBIWXMAAAsTAAALEwEAmpwYAABIKUlEQVR4nO2dd5hU5fXHP2fZBiwuZSkLSBGUtUREiqioC8Hek/gTBcWCKJpYY4qaGFGSKDZUpFgCCoqJglGiQlRWJWBDERugFCnSkbIsbD2/P9477Owwszs7u8xcds/nee4z9763zJly7/d933Pe84qqYhiGYRjhSEq0AYZhGIZ/MZEwDMMwImIiYRiGYUTERMIwDMOIiImEYRiGERETCcMwDCMiJhIxIiI9ReTQGl6jVW3ZYyQGEWkhIneJSOOgstNE5HRv/Tci8rMavkeDmtpZxfUzRKTb/nyPkPfrLyLpIWUniUhKNa5xi4gcVfvWVfqe7UVEvPXsWH8XEcmoXcv2LyYSsbMYeENETo7mYBFpJSJ9QoqnisjVtW+aEUeSgJ+r6q6gsk5AR2+9H1AiIgeLSGZlFxLHiMCDKIj3w50rIs+JyBlB28eE7G8nIu0ivFfwA7kxMFdE2lRmXzSIyDEi0iFMebuge+UPwFEicryI3OSVPQucX423eg33vSSHvM//iUhWBNumBe5Bz55XRKRhNd7zJmCEt34B8FBVJ4jIOBE5PqT4MxHpIiJ9RcT3z+Dkqg8xAETkCFX9JrCtqrtE5HmgWchxLYA0Vf1RRF4EDgXygZbAV8DF3nGHAyVAYZw+grF/KAV2AYjIncCpQDagInIpcDjwD5xovE/5738k8LR37Dbcf+Rw4EtgqYgsBEpUdTug3msozXCVlQAvi8iaoO02wAfANWHO/buIbFPVe3H/wbWqur6yD+rV/j8AyoDdEQ47DvcAvzi4UFXXisjTIlIKFAErgb8BD3kt6q2q+nKY92wC/AtID93n2f12kKYKTpT/KyJnatBIYRHpCvQEPveKioAsVY30OcJRDASeASXAwsoOFpFGwGk4UQymTFWXiUhf4B8i0l1Vi6phR3xRVVuiWICXgbwoluXAjKDzLgEa4Wogg4LKpwJHAScDZ8dokwAjgRa19Bn7AauBB4ArgD8DY4HkkOMuAj6PcO5ooHGYazcFfgsMA0YBm4EbvX2nA3OAW4HtwF3ABO+zHetd9+yga52Fu0mj+t6AvsD9wA3AFGAHrgUQ/HmvBO7x7E8CWgMvAfOBIcBluIfaJiAz6NrNgJlAe6CHV3YdcIW3PgU4rBLb/gLket/PTFzFrRHwf8C93jFzI5z7b+AQ4FBv+38h+68Arolw7iwgJ+i3WRjyW91dzf9Oc+83uxPoHOGYU4Gf4USkL05QDgMGA8ODjssEjgzabg2ket+1eN/Zr7x9jwK5Udj3IDAi5HfLC9pOBxpEuCeSvPVRgffC/Y8Dv3ETXGsy9NxrAp8LuCSo/Mug9VuB7Nq4f/fXYi2JKBCR9kBTVc311jeoarG370RV/V/Qsf8CPhGRNFUtBH4HvAJ0Bj7zjrkQ+ERVv/K2J4jIelVdEPK+I3APts1VmLgT93CrEao6V0SWAS+o6sKAbcAg3MMuwAe4GzXcuVO1YtdLgL8Bf1bVTd51X/KuC3A0cL6q7hCRG4EnVHWbiNyhqp+JyCLcQ+U/3vE5QL6q/ocqEJEc3AMiV1VLgLEi8lfcQyjc550MXKyqL4rIm8Axqjol6HrTgXNE5FPg50AWcDBwLVDs1ZS/B77zTpkMNAhtiYrI00BXXNfUBZS3JJYAK3CtjH1aDyLSG7gP95v3AsYDe0TkSqCNiOQFHd4GJ/Kh10j1vsPxXi08GegSdK54Nr+vqnMifK+iquq1LoZ559wOFKnqnpBjk3G/XSauBt8daAFkAHtwQn2w1/JK8ez+SUROUtXdqrrBu87zwKIw5iR53bbfquq8MLYehKtY9AgqDs1H9CCu4nFzSPk9QGMR2YO7h88WkW2Utxav8D5HKxHpql6LwOsyPBO4WER6AneKyExcBSRJRI7zPmczYJaInKGqP4b5bIkn0Sp1ICy4P8p93np74BPgRNyNUUp5jawTcFTQea2A6d76v3BdTl1wD8vg6x/vXXMk0CbBnzUP92AMbP8JuD3kmGaEqR2Hnhuy770wZc2817ZBZStxggzQynv9PTA76JjrgW1Rfp6XgGtDyjIpr/WHft67gFu89SuAR8NcMzVo/WpgZtD274Gvqdi6XAw8FXKNLFwNdDxOHJqGXOf/gN9665FaEq96/7kM3IO+d8j+luH+T7i+/4uDtrsB70TxXbbEVQ6a40TsYFwNfwnu4X48rjunPXBqyLkZuPvlMWAdrn+/G67V9DHQ1TtuOPCHMO99ATANaECElgSuQvUm3v0YdO7duC694HuzKV5LAtfafx9oGOZ9ZwOdvPX7CN+SOAaYFHLeVcBp3vpE4Ffe/yAPJ/7nA0eGe0+/Lb53mvgBVb0b+JOIJKvqGlyt4xzcw2a9qi72jlupXuvAoxvlfcZZuH7cXsD/ROQyETlHRM4BBuBqMF8DW+PxmaLBqyUOwHUPBMouwbUK7qjm5b4VkUe8PmYAVPUn7zVsDUpVN3qra4Fsz7GbQpR+HM8peBbwTsh1t6vq52GOT8O1DsK2ULyWHVqx//hkoJmI/E5chFOkvuWPQmzYrKo7cTX678IcXwz8EOFawfTGtcRuAx4TkbzAgquYfCsiJ4Wc01lVXwra7gCsCr1wsFM8iLNUdSuuu/Q177vYhBO6W3APvzJcKyX4t87HCeibuO/iXeBFXFfen3FiB65rKdivgogcApwBDFHV0nBfgohkA08C43BCFChvhuvi+iHk3gzsPwn4NXCuhvdPhH2/yvCCBUYCfUTkVpy/52VVzVHVXNx9PltVv1bV3SLSubrvEU+suyl6koAnReQTXC3qDVyNKdwNHmAAcJGInICrbXwF/J+qfiwufG+t96CcKSJDNKhbo6YEugNiPP10ETka1x00RlWXBHao64ZZh6tlV4ebcP3VX3ldOo8ERCJKFuMeqE2Avd1ynmh1D3P8RuAFXA12XRXXPl1EzvOuf6OqLg3ad5iIDAIaAn/EPYQC752CE/+fcC2g+3E+qbs1yAnrdUnsE94pIr8CpqlqidclE1xpewvI8h5yjcOc2wNXE92pqvO8CJqHVPVlEekREEEReQ+YG3TeE8CJInJB0OVaARkhXVWNgN4i8idVvc8rKwYKAFR1joj81RPibJx/7WJVVXHRRas9EURcZNb5uG7X5cDZuPtmANAfV1sfAbyNC/SYFWRva5xwj8Y5pAUnKLtE5Ne4LrtcXNfNOFW9R0T2no+rfP0GJ06hdMFVds7W8IEBEINIqHPSX45rNTyJ++8HPk8SrovtVK9rMhkYJiJPq+q/q/te8cBEIkpUtVREbge+Bd5VF53QhRCR8B4cZV6NZxwwQb2oEe+GXSQiP8fduA8AN3k10GdEZGG42k518fqcvxSRIuDoGMRilqouFJGpwGQRyaipgKnzz/xZRMbg+q4Xicj5qvpZlJeYj/NLJOGihQLXfRFXI90HEWkbOKyKa8/CtfKeUdUvQ/YtVdVp3vVah+z7Je4hMAJXa1+P63K5R0R+ixO0TbgH2OMhth0NdFDVhwNFuBbmL3Ett1JcxeJe4L9B5+UAf8cJ02rKW6plQZd/S0T6qOoP4EKjgvbdDRSr6o6ga47FPdBUw0QYVcJO3AN4N662vgbXbVMSclyxqj7nvddpON9AKU4QXvWEJTBm6EhcZSr43Ke8c8/G/ZbDVXVMkP3HAU/gfovAfy3wHf9HVdfJPlHFXI4TvF+qakEln7EUmBbkkzgngk+iwn2rqu96z4tncb6qq3B+p0Jc11exqr7p/Z6DNLwfzxdYd1MUiMhkr5b1b9yfY5K3/TBwWkgTfyXwkoikqupGVV0vIilek7KBOqfeFbg/ezfvT3I88FJtCISH4v6MO6o6sNKLOKEbi+sOqBVUdYuq/gHXlz+xGqd+iBMJUdWyqg722IK7ycPGzYfYtQRIFpHDKjns1ZDtNsA873xV1fdxXVsDcP3mM7zuhTOBPHFBDwGnpgQJBOoc+k/jvmvxunFmAbeq6u1B77kaeFlVr6Ti75vsXTsT2BQQCELuce/7DxaIVMq72NqIC+GujAYiMkRcgIZ6D+ttOPG5VUTC/aapIvKViMwHnsP5Mf7rfVeBsRONxY2v2B380Pa6tgLrBV6X0Pfigj/wKmpP4h72m0I+6yJV/Ti4TEQaiwtc2AOsq0IgwIn3IO93fB642VsfDfzdWw847oPf53ZcyO0huC61Zar6T6+18D+grbhBdTNwkXW+xVoS0XErIX9eABF5F+dk+zj0BBE5RERG4W7kT3HdU4ERmjuBX+D6JvvjYuhrHJ0UQF3k1dG1dLkdOGGMCRHppKorReQCVX01UK6qs0VkfDUutQBXW/yoqgOD3qNQRD7HifDePnfvYd1eVT8MOeV5nICH9bcEfE/eNXriaonJblMOxoWw5uO6ZbKBNBHp553SBlgtImd7AvCFV3F4lvIHTDLuvzDbq/m2BjqKSF9VXeTZsIuKkWYBUr3XXCp2rSSLSFIlwnoVrsurQET+CYwWkWGVHF+K6/66Qct9Rqhqkdf19x6uBUXQvm24wXMZuECCc3F+kIdV9T3vsA+8fVW2ZFT1P15XVytc+OxFqrqPTyUUcSPf++IqdyXApSH7GwInqersoOJoR1WHVriPwbWwvg7TOv0YuNF7/3Gq+kSU75EQTCSiQFW3hJaJyM24rqcjxIUffoar4QWcsctF5JGAgIhIJ1zXA8AyXBP1dVzf+bVh/khR4f2xnwPuV9VPvbI04AtghaqeGct1gzgFN4YhFtuSgaG46LChIjIv8GDxao0Lo72W98AvxoWXVocHgWtE5F+qWub1CV9K+NGy04CPReSuqlor6oUrezVvUdXV3sN8t1f+JvBXXITWKxGusUJELsMFP5SISFNc90tuNT8juJr5elxr5HYRGYb7v10c6bN4tfBf4h7aqOpGEXkDeFZErvEqG+HsnuCdfzHu/7HTK9/tiWKjCDZ2w90zfwQG4nxUAZbgHuDXRvl5v8J14f0D17qqiiTvHvvSs71pmGMa4brAakMkLgv+3r3/ewNVXYHzy/wCuE1VH/P2X+J1nfoOE4lq4tUYf4urYf/Ge/C8iHOQfS8iowLdCOoc1INwN+uvcA9ucIOO9ngPhva49AJpQHfvnFTKnY1VNYcD3CQiV3k3dimuphRtt0zgs52Oc+Zd6tW6snB9xFcHHXMJboDRsSIyBPhAVX8Qkf5B5x6Fc/QOwjlgwfWhfyoik3DRSh0pT3EQuPal3nsOF5Gx6ka1HwX8nzh/znu4h/gluO6Js1T1jco+k6q+5NVgnxeRVbiuuPHqfEz9PJsHi8i3qrpF3JiMv4vIY7iIms4icnSgJh+GBngPiCCBGAwsVtUPROR2caknxqobpxFq35rQslBEZLCqTg3aPh5Xm+9KuWP1B5zj/F5V/QLXUhkGvCMiwzVkvIN3jRtxg7z2jmtQ5/g+Avc9/0ZV5wadViGFhffdCq5mHigrEjfSeJ+ubFVd4P0Gj+C6miaKG6W+CBcN9H/AcyJyeaTvW1w00hW4UNs2uK6eeSLyKq4l92UEUQwNHCgF2oaUdcL9154O+DVwv280Pom9lRdxvqvzxOXDSsL9Nh/ixk+dj3tWjPbeayKuFXh4uM/rB0RjDoCpP3g3TQ7uTym42t7aMMcdg2tCDwgqa4JzRP4M57C7DdcUDdAcJzhZuAFGR6k3eKiGNtckusmIEnGhl8+p6qleK+UG3APkLq8CIbgHwq9w/c934PrDpwCheZWScVFCC4PKDsL9b+5Q1b9579kAN1YkMLDuWFzc/4saEqXjifd5qnqLt90cNy5gD/CPSloZN+MCK6bgupZ2iwvXfpby1BSRSAZSVPW4oOsdhEvV0QqYqKqbvMrQjd5n/Kuq7hGRgbgW3QxcSG2B9/mPwt0fS4E3NSgU1rvHLsf1/R/unXurBkXPich3uHurMKhspnfdwGDVNriH+aCAoIvrUr4yyMcT7rs6BtcbMCLInkeAJ9ULzBCXt+osXC/Cs14l5Ubc6PxZwHZV/UsV32tCMJGIAhFpFdz/GsP5nYAdqrrVu0lLNMh5aBy4eN1Np+EenIcDH3ldCqHHHQ/8GHjYeP3pu7SGUS3iAiSiyvsjLvLueGB+pK6kkOMPB5aEdJs00ypCl72W2zWq+oi33Yry72aPV3YQrkX6jaquDDm/G7DZa9m1wA1erCqMOXBuU1wrPXTU9+XAlKq6EcNcr4uqLqvimAYaYfyGt78j0EhVvw2z72Rcuo/1qnpRdWyLFyYShmEYRkQsBNYwDMOIiImEYRiGERETCcMwDCMiJhKGYRhGREwkDMMwjIiYSBiGYRgRMZEwDMMwImIiYRiGYUTERMIwDMOIiImEYRiGERETCcMwDCMiJhKGYRhGROrUfBJZWVnaqVOnmM7dtWsXjRvvM9+8rzAba47f7QP/2+h3+8D/NvrNvgULFmxW1Zbh9sVFJESkDXAe8CNusplxuAnUtwGZQbMz3RRNWSQ6derEp59+GpONeXl55ObmxnRuvDAba47f7QP/2+h3+8D/NvrNPhGJOF9GvLqbBuPm0Z2Jm2qwF5CtqpOBZiKSIyJdoymLk72GYRgGcZpPwptw5Qrgd7iZu/KBNao6XUQuwE34rriJRiotC8yvG3Tt4cBwgNatW/ecNm1aTDbm5+eTkZER07nxwmysOX63D/xvo9/tA//b6Df7+vfvv0BVe4XbFy+fxKfAmcCrwJu4OWEDM7Pl42atEtwE4VWVVUBVJwITAXr16qWxNuH81vwLh9lYc/xuH/jfRr/bB/630e/2BRMvkfg1MBa4G3gGWAI08fY1AbbgBCGaMsMw6hjFxcWsWbOGPXv2VH1wFGRmZvLtt/vMFuobEmVfeno67du3JyUlJepz4iUSGcBGVVURmQF0xk3iPgPoDrzgHXdFlGWGYdQh1qxZQ5MmTejUqRMiUuPr7dy5kyZNmlR9YIJIhH2qypYtW1izZg2dO3eO+rx4Oa4nAVeIyDlADi66aYOIDAW2qupSVV0aTVmtWzZ1KnTqxCkDBkCnTm7bMIy4smfPHlq0aFErAmGER0Ro0aJFtVtrcWlJqOpq4B/e5kzvdUyY46IqqzWmToXhw6GgAAH44Qe3DTB48H57W8Mw9sUEYv8Ty3dcv0dc33knFBRULCsocOWGYdRbQqM+S0tL964vWrRo7/ru3bsj+hZ27NjBggULWL16Ndu2bdsvdsaD+i0Sq1ZVr9wwjDrLV199xYsvvgjAzTffzO7du1m4cCEAt956KwA//vgjDzzwwF4RmTdv3l7RWLq0Ym/45MmT2bVrF82bN+ehhx4CnNgsX76cyti5cyePPVbpuOG4Ur9FokOH6pUbhuEPPF8iSUm15ks86qijaNCgAeC6ZRo2bMjkyZMBaNasGQCvvvoqDz74IN988w19+vThmmuu4cknnyQ3N5errrqKHTtcZP/WrVuZPn06SUlJLFiwgJ9++om5c+fyzjvvsG7dukrt+PTTT30VmVWncjdVm1Gj9vok9tKokSs3DMOfBPkSgVr1JQ4YMACA5GT3aGzYsOHefRs2bCA5OZlmzZrx6KOP8tFHHzFy5EhuueUW0tPTSU1NBVxr4fXXX6dJkybcddddqCo//PADX331FatWrWL27NmV2tC/f3/ee++9Gn2O2qR+i0TgD3XnnegPPyANG8LEiea0NoxEcvPN4HXzhOXDD6GwsGJZQQFcfTU89RQADUtLwWsVAHDMMfDooxEv+Z///IdRo0bRsWNHXnzxxb0iEezoXbFiBW+99RazZs3ioYce4oQTTqC4uJgZM2Zw2223cdlllwGwatUqhgwZwqJFixgxYgSlpaWMHTuWG2+8kdWrV9O+fXuKi4ur8YXAU089RZcuXViyZAlXXXUVaWlpTJs2jYyMDCZPnsz999/PIYccErasptTv7iZwgrByJVtOPBE6dzaBMAy/EyoQVZVHwdlnn828efPo0qULAGVlZUBFkejbty+jRo3i4Ycf5uCDD2b37t17U2s89NBD9OnTh6VLl9K5c2caNGjAd999x7Bhwxg+fDivvfYaw4YN44477uCBBx6olm2zZ8+mXbt2DBgwgNzcXCZNmgTABx98QG5uLk899dTeFk+4sppSv1sSQRQcfDB8/DGUlECyfS2GkTAqqfEDzgfxQ5ikpR07Ql4eALtrMFittLR0r28iOMpp/vz5jB49mssuu4zCwkLeeecdGjZsyMcff0xpaSk///nPK1znyiuv5MILL+Szzz5j7dq1nHvuuQAsWbKkWi2JhQsXcsEFFwDQpUsXHn/8cQB+85vfcOONN5Kdnc0999wTsaymWEvCo+Dgg6G4GFauTLQphmFUxqhRzncYTC35Enfs2MF7773HQQcdBJS3KAD69OnDs88+S69evdixYweTJ0/mtddeA2Dz5s2MHj2awsJC/vWvf3H88cczZswYTjnlFE488URuuOEGcnNzyc3N5dprr2Xu3LlR23TUUUexyou4XLVqFUceeSSqyp49e3j22Wfp27cvb731Vtiy2sBEwqMgENG0eHFiDTEMo3IGD3a+w44dQcS91oIvcceOHXz55ZesWbOGK6+8EihvSZSUlLBgwQJ+97vfsWjRIjIzM0lLS+Piiy+muLiYNm3acOyxx/LII49w0UUXMX/+fGbNmsXAgQOZOHEi999/P0OHDmX27Nnk5eXRv3//iHbk5eWxaNEi8rxW0ZlnnsmyZcv473//y7vvvsvVV1+NiHDbbbcxc+ZMtm7dyjHHHBO2rDawfhWPgoMPditLlsA55yTWGMMwKmfw4Fr3Hx500EGMGTOG/Px82rRpA8B9990HuASExx57LOPHj6esrIy8vDxuuOEGPvroI0aNGsW0adNo06YNRxxxBACffPIJ33zzDbfccgtffvklGzdu5NRTT2XkyJGUlZVxwQUX0KdPn7B2BFocAUSEa6+9dp/j3nnnnajKaoqJhEdJZiZkZTmRMAyjXnLIIYdUmOchEOX017/+da+fIikpiYEDBwJw3HHH7a3xA2RnZ7N27Vq6du1K7969AWjbti3t27cnKyuL++67j1WrVpGZmRmnT1RzTCSC6dbNupsMox4TaSKgBsHhtFXQrl27CtsdO3assN2hQwd27txZfeMShPkkgsnJsZaEYRhGECYSwXTrBhs3wk8/JdoSwzAMX2AiEUy3bu7VWhOGYRiAiURFcnLcq4mEYdRbgtOCv/zyy/ukDY8WSxVeF+nc2Y22Nue1YdQrvvnmm72hp4MGDQJg48aNvPbaa2zfvn3vcQWh888EETp4ra6kCrfopmBSUqBrV2tJGEY94/DDD+e3v/0tp512GqNHjwZg0qRJPPLII8ycOZMhQ4YAbqBbcXExO3bsYOLEiTRo0ICVK1funZv7iCOOoEOHDntThffo0aNCqvCCggIaN25My5YtI9piqcL9joXBGsYBwbp1MGgQvPQSeGPfYkZE+Pjjj+nduzctW7Zk6dKlHH744bRo0YJ27doxYcIEhg8fzllnncWiRYs4+uij92Z9/ctf/sJf/vKXvdeqa6nCrbsplG7d4PvvXaI/wzB8y733wty5MHJk7VwvJSWFlStX0qZNG5YvX05aWhoffvghy5YtIz8/n/fff59p06btnTRo1KhRe/0VpaWle2emC6QKP/TQQ3n66aeZOHEi5513Hk8//TTPPPMM7du3r7ZtTz31FO+++y7jxo2j0Mt2O23aNGbOnMlFF120twsrXFlNiUtLQkSOAcYD3wNZwBNAF2AbkKmqj3nH3RRN2X4lJ6c80V/Xrvv97QzDqEhV00l88AEE5d1j3Di3JCXBSSe5stLShtWZToL8/HymTJnCq6++ytatW5k/fz4lJSUkJydz3HHHMX36dE455RTy8vIo8SqQxcXFiAhvvfUWeXl5tG3blueff57OnTsD7E0VHmhJLFq0iMLCQs4880xuueWWqL+P4FTh2dnZTJo0iWuvvZYPPviA+++/n379+rF7927vu9m3rKbEqyWRApysqkOA54DFQLaqTgaaiUiOiHSNpmy/WxoIg7UuJ8PwJX36QKtWThTAvbZqBccdF/s1MzIyWLhwIQsWLOCyyy5j5cqVNGnShMcee4zFixfz5ptvRjz3jDPOIC8vjxdeeKHCyOwrr7ySvLw8HnnkER5//HHy8vKYP3/+3qSA0bJw4UK6ehXWLl268MUXXwDlacFHjx6918cRrqymxKUloaqfAIhIO+AnYADwsbf7C+AUQKMsq/D0FpHhwHCA1q1bV8ijUh3y8/PJy8sjeft2+gHfv/EGayIM0U8UARv9jN9t9Lt94H8b94d9mZmZe1NV3Htv1cfffHMakyalkJ4ORUVw7rnFPPJI+aRDwXNCBKgsE8Z7773HqlWrOOyww9iyZQvNmjXjoYceYv369cyePZvbb7+dnTt3UlBQwPr163njjTcoLCxk586de18DzJgxgyeeeIK0tDQeeughFixYQFZWVoXJhm677bZ95p8IJviahxxyCIsXLyY7O5tly5bRpUsXduzYwZYtWxgzZgxvvvkm06dP54wzztin7Mwzz9zn2nv27Kne76eqcVuAPwANgDuAgV7ZQOCP0ZZVdv2ePXtqrMyZM6d8IytLddiwmK+1v6hgo0/xu41+t0/V/zbuD/u++eabah1/4YWq11+vunChe73wwor7d+zYUW0bvv32W12yZIkOGTJEy8rK9OWXX9bCwkJ94YUXdMuWLaqq+vrrr2vv3r21qKhI7777blXVva+ff/65FhUV7b3enj17dOTIkfrcc8/pCy+8oM8++6wWFhZWad+cOXP0wgsv3Ps9l5WV6fjx43X27Nk6YcIE3b17t6qqDhgwQF9//XWdNGmSrl69OmJZKOG+a+BTjfBcjVt0k7h5ADuraqmIbAEC00Y1AbYAEmXZ/qdbNwuDNQwfM316+frYsbVzzaysLPr27cvtt9/OkCFDWLt2LY8//jjbtm3jtttu4/nnn+fEE0/kD3/4AyeeeCKNGjXam9I78HrnnXdy6qmnWqrwGDk06P3mAFcAM4DuwAteebRl+5ecHHj99bi8lWEY/iArK4t58+bRqlUrLrnkEpo0aVJhjusAv/jFL/jFL34R8Tp1LVV4PENg04GdAKq6FNggIkOBraq6NNqyuFhqif4Mo17SqlUrwE1AFE4goqFdu3Y0a9Zs73bHjh05ODCpGS5VeFLSgTP6IG4tCVVdBNwctD0mzDFRle13gnM49e0b97c3DMPwCweOnMUTC4M1jLijMSbSM6Inlu/YRCIcnTu7PE7mvDaMuJCens6WLVtMKPYjqsqWLVtIT0+v1nmWuykcKSnQpYu1JAwjTrRv3541a9awadOmWrnenj17qv0wjCeJsi89Pb3aaUFMJCJhYbCGETdSUlL2prOoDfLy8ujRo0etXa+28bt9wVh3UyRycizRn2EY9R4TiUh06+YS/a1YkWhLDMMwEoaJRCRsKlPDMAwTiYhYGKxhGIaJRESaN4esLGtJGIZRrzGRqIycHBMJwzDqNSYSlWHzXRuGUc8xkaiMnBzYtAm2bk20JYZhGAnBRKIyAs5r63IyDKOeYiJRGSYShmHUc0wkKsMS/RmGUc8xkagMS/RnGEY9x0SiKiwM1jCMeoyJRFV062aJ/gzDqLeYSFRFTo4l+jMMo95iIlEVFuFkGEY9Jm6TDolIfyAVGATcBlwGbAMyVfUx75iboimLK8GJ/s45J+5vbxiGkUji0pIQkZbAYao6CxgBNAeyVXUy0ExEckSkazRl8bC3As2bQ8uW1pIwDKNeIvGYeFxELgOOAH4EugHfAatVdbqIXAC0BhTYXFWZqk4IufZwYDhA69ate06bNi0mG/Pz88nIyAi775gbbwRg4WPxb8gEU5mNfsHvNvrdPvC/jX63D/xvo9/s69+//wJV7RVuX7y6m9oCq1R1nNftdBLwtbcvHzgcEGB5FGUVUNWJwESAXr16aW5ubkwG5uXlEfHc446Df/878v44UamNPsHvNvrdPvC/jX63D/xvo9/tCyZejuvdwFpvfQ1QCDTxtpsAW7wlmrL4062bJfozDKNeEi+R+ATo6a23wXUj9fa2uwN5wJwoy+KPRTgZhlFPiYtIqOp8ABH5Fc438RCwQUSGAltVdamqLo2mLB727oPNd20YRj0lbiGwqnp3SNGYMMdEVRZ3Aon+LIeTYRj1DBtMFw3JydC1q7UkDMOod5hIRItNZWoYRj3ERCJacnJg2TJL9GcYRr3CRCJaunWzRH+GYdQ7TCSiJTiHk2EYRj3BRCJabKyEYRj1EBOJaAkk+rOWhGEY9QgTiepgU5kahlHPMJGoDt26mUgYhlGvMJGoDpbozzCMeoaJRHWwHE6GYdQzTCSqg4XBGoZRzzCRqA6BRH/WkjAMo55gIlEdLNGfYRj1DBOJ6mKJ/gzDqEeYSFSXQKK/4uJEW2IYhrHfMZGoLpbozzCMeoSJRHWxMFjDMOoRJhLVxcJgDcOoR5hIVJdmzaBVK2tJGIZRL0iO1xuJyP+AQEf+n4FzgW1Apqo+5h1zUzRlCcdyOBmGUU+IZ0tinKoOUdUh3vtmq+pkoJmI5IhI12jK4mhvZCwM1jCMeoKoanzeSOQJYAlwGPAlsFlVp4vIBUBrQKMpU9UJIdcdDgwHaN26dc9p06bFZF9+fj4ZGRlRHdv+n/+k67hxzH31VUoyM2N6v1iojo2Jwu82+t0+8L+NfrcP/G+j3+zr37//AlXtFW5f3LqbgLGq+q2IXAlkA8u98nzgcECiLKuAqk4EJgL06tVLc3NzYzIuLy+PqM/Nz4dx4+jXsiWccEJM7xcL1bIxQfjdRr/bB/630e/2gf9t9Lt9wcSlu0lE0oFAfu01QArQxNtuAmzxlmjKEo+FwRqGUU+Il0/iDOBib/1g4E2gt7fdHcgD5kRZlng6dbJEf4Zh1AviJRKzgd0ich5wkKp+DGwQkaHAVlVdqqpLoymLk72VE0j0Z85rwzDqOHHxSahqAfBUSNmYMMdFVeYLcnLg228TbYVhGMZ+xQbTxUq3bvD995bozzCMOo2JRKzk5EBJiSX6MwyjTmMiESuBHE7mvDYMow5jIhErlujPMIx6gIlErFiiP8Mw6gHVFglxHBy03VNETq1dsw4QLIeTYRh1nFhaEvnAIABPHCYAR4rI7bVp2AFBTo61JAzDqNPEIhJjVXW0t/434FJVfRTYUGtWHSh06wabN8MWf2QLMQzDqG1iEYkFACJyDPBT0CjohrVl1AGD5XAyDKOOE4tIHCIiPwN+BzwBICJJwC9r07ADAguDNQyjjhOLSDwDDAHeU9V/i0h7YDxu7of6RSDRnzmvDcOoo1Q7d5OqbgR+H7S9Bm/Sn3pHcjIceqi1JAzDqLPYOImaYmGwhmHUYWIZJ3GZiPzOGy/RQETGiMhUbz7q+kdODixbZon+DMOok8TSkrgbeEHd5Nh/AvYAN1AfHdfgWhKW6M8wjDpKLPNJPKyqa0QkDbgcOEpVC0RkTS3bdmAQnMPpsMMSa4thGEYtE0tLYof3eibwhjehEECH2jHpAMPCYA3DqMPEIhJNRORh4B7gEQAR6Y7rcqp/BBL9mfPaMIw6SCwhsONE5HDgPlXd6o2TOAmof7mbAlgOJ8Mw6igxzXGtqt8Gra/BG3ldb+nWDWbMSLQVhmEYtU5MIiEiycC5wCHACmCmqhZFcV5n4LeqeoOI3ARsAzJV9TFvf1RlviM40V+LFom2xjAMo9aIZZzEEcDbuC6mBkBf4FUROSSK048DGntjKrJVdTLQTERyoi2rrr1xwRL9GYZRRxE33KEaJ4g8APxJVQuDypKA24JSiIc773TgQ2AMMA/YrKrTReQCoDUu91OVZao6IeS6w/HSgrRu3brntGnTqvV5AuTn55ORkRHTuQ3XruW4IUNYfPvtrD/rrJiuEQ01sTFe+N1Gv9sH/rfR7/aB/230m339+/dfoKq9wu2Lpbvpm2CBAFDVssrGSYhIG2CXqm4XEYAsYLm3Ox84HJAoyyqgqhOBiQC9evXS3NzcGD4S5OXlEeu5lJTAVVeRI0JOrNeIghrZGCf8bqPf7QP/2+h3+8D/NvrdvmBiCYGNNGKssrQc3YFkEckF2gC7gCbevibAFm+Jpsx/JCdD167W3WQYRp0jFpF4XURmiciNInKRiFwnIi8D70c6QVVnqWqequYB64E3gd7e7u5AHjAnyjJ/kpNjYyUMw6hzVFskVHU+bo7rZCAXV8O/XlXfq+w8LyHgr4Ajcb6GDSIyFNiqqku9Ge6qLKuuvXGjWzdL9GcYRp0j1nESPwEPB5eJSENV3V3JOQq87C3gHNihx0RV5ksCif6WLy9P1WEYhnGAU5vzSVxYi9c68LAwWMMw6iBVtiREpB/RickJwAs1tuhAxRL9GYZRB4mmu6k/8AtgYRXHda+xNQcyTZtC69bmvDYMo04RjUiMBVao6pTKDhKRwbVj0gFMt27WkjAMo05RZTeSqm4FPoniWotqbs4Bjs13bRhGHSMqx7WqVlk9VtUva27OAU5Ojkvyt3lzoi0xDMOoFWozuskw57VhGHUME4naxMJgDcOoY5hI1CadOkFqqomEYRh1BhOJ2qRBA5foz5zXhmHUEUwkahub79owjDqEiURtY4n+DMOoQ5hI1DY5OeWJ/gzDMA5wTCRqGwuDNQyjDmEiUdsERMKc14Zh1AFMJGqbQKI/a0kYhlEHMJHYH1gOJ8Mw6ggmEvsDC4M1DKOOYCKxP8jPd4n+kpLcKOypUxNtkWEYRkyYSNQ2U6fCK6+4dVX44QcYPtyEwjCMA5JoJh2qMSKSClwG/AR0UNVHReQmYBuQqaqPecdFVeZr7rwTCgsrlhUUuPLBNi+TYRgHFvFqSRwONFfV6cDBItIHyFbVyUAzEckRka7RlMXJ3thZtap65YZhGD5GVDU+bySSpKplIvIY8DWwSVWni8gFQGtAgc1VlanqhJDrDgeGA7Ru3brntGnTYrIvPz+fjIyM2D5cEH0HDSJ9w4Z9yve0bMmH//xnja5dWzbuT/xuo9/tA//b6Hf7wP82+s2+/v37L1DVXuH2xaW7KYCI3A4sBloAy7zifFxLQ4DlUZRVQFUnAhMBevXqpbm5uTHZlpeXR6znVuChh5wPoqCgQnF6aSm5jRpBnz4xX7rWbNyP+N1Gv9sH/rfR7/aB/230u33BxM1xraplqjoaKAV2AU28XU2ALd4STZm/GTwYJk6Ejh1BxL3eey8cdBD06wfjxjmHtmEYxgFAXERCRPqKyBBvcz2uG6m3t90dyAPmRFnmfwYPhpUroazMvd51FyxYAAMHwvXXw9Ch+7Q0DMMw/Ei8WhIrgLYich5wLPAssEFEhgJbVXWpqi6NpixO9tY+zZvDzJlwzz0wZQr07QvffZdoqwzDMColLj4JVd0APOBtvua9jglzXFRlByxJSfDnPzu/xODB0KsXTJ4MF1yQaMsMwzDCYoPpEsEZZ8Bnn8Fhh8GFF8Lvf+/moDAMw/AZJhKJomNHmDsXrr0WHngATjsNwoTOGoZhJBITiUSSlgbjx8OkSTB/Phx7LMybl2irDMMw9mIiAaxbBzfddAzr1yfIgKFD4cMPoWFDOOUUeOwxC5M1DMMXmEjghjF8+WUmI0cm0Iju3eHTT+HMM+Gmm+DSS102WcMwjARSr0WiYUM33s2NbxPGjXPbDRsmyKCmTeHVV+Gvf4V//hOOO84mLzIMI6HUa5FYvtxV2INFoXNn+PrrxNlEUhL88Y8wezZs2gS9e7uWRadOnDJggM1PYRhGXKnXIpGd7bJlFBZCamopIrBiBZx/vg8mlvv5z12YbOvWzkfxww+IzU9hGEacqdciAS7q9LrrYOzYzxgxAk44wTmye/WCl15KsHHt20Nx8b7lgfkpDMMw9jNxzQLrR6ZPd695ebsYNsytr14Ngwa55YMPXGLXtLQEGbh6dfjyVatcBJRIfO0xDKNeUe9bEuE4+GDIy4Nbb4WxY+Gkk1yevoTQoUP4clXnr5g508JlDcPYb5hIRCAlxbUgpk+HpUuhRw94/fUEGDJqFDRqVLGsUSO45hr46Sc491wTC8Mw9hsmElVw4YUuy3fnznDeeQlIsxQ0P4UG5qeYONEtixfDs8/C1q1OLPr0gf/8x8TCMIxaw0QiCrp0cdkyAmmWBgyAH3+MowHe/BTvvfuu6/caPNiVp6TAlVe6UKxnnoEtW+Ccc0wsDMOoNUwkoiQ93aVZmjLFtSx69IB33km0VR4pKXDVVfuKxXHHwRtvmFgYhhEzJhLVZPBg+OQTyMqCU091KT3KyhJtlUewWDz9tBuMd/bZJhaGYcSMiUQMHHEEfPyxE4w//9mlW9q0KdFWBZGSAldf7TzuwWLRty+8+aYbiNepkxvdbSO4DcOoBBOJGGncGJ57DiZMgPfec91PvsvyHRCLJUvgqafcyMGzzoLLL3cjt20Et2EYVWAiUQNE3PN1/nznszjlFHj4YffsXbfObScs/XgwqakwbJhrWTRvvm//mI3gNgwjAiYStUCPHs6Zfe65cNtt8Mtfwl13uYnnEpp+PJTUVDe2Ihw//ABPPOHUzTAMwyMuIiEi6SJylYicKyL3iUiSiNwkIkNF5Mag46Iq8yOZmfDKK5CcDDNmuOELZWUkPv14KJFGcKekwG9+A+3aQW6uM9ymUzWMek+8WhJnAKWq+jqwDugFZKvqZKCZiOSISNdoyuJkb0yIuJRKp55anlIpKckldF2+PLG27SXSCO5//MPlSL/7bti4Ea6/Htq2dcZPmOAzz7xhGPFCNA5hkSLSFGihqstE5D5gB/C9qk4XkQuA1oACm6sqU9UJIdceDgwHaN26dc9p06bFZGN+fj4ZGRkxnRvKI48cyuuvtyUpSSktFUD42c+2MXToSo49dlvMOflqy8ZWb7/NIU8/TdrGjRS2asXyYcPYOHBg+QGqNF65kpZz5tAqL49Gq1ejSUn8dOyxbMrNZVO/fpRkZu5XG/cXfrcP/G+j3+0D/9voN/v69++/QFV7hd2pqnFbgK7AFcAdwECvbCDwx2jLKrt+z549NVbmzJkT87mhXHih6vXXqy5cqHrttardu6u2b68Kqv36qb79tmpZWWJtjJqyMvdB7rhDtUsX9yGSk1XPOEP12WdVt251x02Zotqxo5aJqHbs6LZ9SEK+w2ridxv9bp+q/230m33ApxrhuRo3x7WItAF6qOokYAvQxNvVxNuOtsz3TJ/ussd27+5GaS9cCN9/D08+6bJqDBwIJ5/sRmz7fnybiPsgo0bBd985D/1tt7mw2quucpMi9ejh1m1iJMOoc8TLcZ0KnKWq/xKRFOBDoLe3uzuQB8yJsuyAJC0NRoxwYjF2rJsB74ASC3CCceyx8Pe/w7JlbkThTTfBl19CUVHFYwsK4I47EmOnYRi1RrxaEsOA00VkCvAuUAJsEJGhwFZVXaqqS6Mpi5O9+420NOcTXrbsABYLcILRuzeMHh05L8mqVa5FMWMG7NgRX/sMw6gV4jIznao+CTwZUvx1mOPGRFNWFwiIxdVXu5x8f/2rE4t+/eAvf3GZZg+YSec6dHBdTKE0bOjmgH3qKRcbfMIJLofJGWe4LqwD5gMaRv3FBtMlmGhbFuvWwU03HeOPEdyhRAqrfeop2LzZ5S25/XbXmvjjH50Po21bl+b8pZfcfBiGYfgSEwmfUJVYjBwJX36Z6a8R3AEiTYw0eLAbpHfyya6p9PnnbiKOSZNczpJ//9tNJN6ypWtljBzpUuyWlVkSQsPwCSYSPiMgFt9/77JkzJ3rxGL8eFAV/43gDhBpYqRQsrNh6FCYNs0N0Js/3+UwKS11/Wx9+rjh60OHWhJCw/ABJhI+JT0dbrjBjdTuFTLEpVkz58v4739h9+7E2FcrNGjg0pffcw989JEb6T11qhOG0tKKxxYUOPV8+WX3pRwwHn7DOLAxkfA5nTs7kUhKguTkMkRca2PCBDjtNGja1Dm5R42CDz+M8/zbtU1WFlx6qROEcOzYARdd5OaTbd7cpQy5/XZ48UWX4dY3sz8ZRt3BROIAYMMGuO46GDduASNGwPHHu2Sub7wBv/618/vedZcrb9ECzj8fHnvMpWI6ICvckZIQdugAn37q/B0XXww7d8Ljjzth6dbNdVOdfDLccgs8/7z7AgItEs/HccqAAebjMIxqEJcQWKNmTJ/uXvPydjFsWHn5mWe6BVz3/rvvOif3O+/Aa6+58jZtXIU7sASev+vWOZ/xSy+5Y3zFqFHOBxHcomjUyDm/e/Z0yzXXuPLiYvjmG/jsMzca/LPPXDMr0A/XqJGLpFq5EkpKECj3cUBk34lhGICJRJ2hZUtXub74Yre9YkW5YPz3v+UV50MPLc9KG5jv4snQESyJJvDgvvNONyCvQwcnHOEe6CkpbsxF9+4upBZcn9uSJeWiMX78vv1wBQVOKNaudecefbRTSxu7YRgVsO6mOkrnzm4yuhdfdLPjLVrkZs1btsw9M2fPrjjfRUoK/O9/kd0BcceLlqKsrPJoqXAkJ8ORR7ppWh99dN+UIQEKCuD3v3eD+9q2dXmoBg50uakmT3ZJtwoLw59rIbpGPcFaEvUAEfjZz9wyaBDceqvLlFFY6AKMUlNd70y/fm77qKNcxo0+fdzrkUc6ETlgiTQivGNH19r48kv44gunpF984ZpWe/a4Y5KTISfHtTQCLY5ly+B3vytXVOu+MuowJhL1jOxsFxFVXOzCbIuK4Ior4E9/cuPYPvnE5e175RV4+ml3Tnq6y+sXLBxdu+7bMxMYFT5rls/8HJF8HKNGOU9/bq5bApSUuIy3ixaVC8cHH8ALL0R+j4ICN5rcRMKoY5hI1EMC0VLDh7tAoXXrnHicd55bwEVFLVtWLhqffOKOHeNl0mrWzIXmBkSjd2+4777yUeG+8nME+Th01SqkMh8HuNbD4Ye7JeDkARdG9uWXFQUlmNWrXT/fkUe65ljgNSfHh6MfDSM6TCTqIYFoKXApQMIh4loLXbvCJZe4spISF1UaLBx//3vouDc3KnzcONeNtWKFE6CE+4MHD4bBg3kvL4/cSA/5qmje3KUT6dgxfPdVZqaLQ/7qK+f0KS525UlJbmxHqHgcdpj7kgJMnQp33skpVTnrDSOOmEgYUZOcXB5IFAjFLSiAt992g6a/+KKiYBQVQbt20KSJG8aQk1Nx6drVDQyMhG/DdCN1X40dW/5QLy52uVW++sopa+D19dfLv6TkZCcURx7pymbOhKIiC9M1fIWJhFEjGjVyXVRvvumCgVJTSykpacDgwS790uLFLhp18WKXDHbKlPJzk5Jc70ywcATEJCsL7r3Xp2G60YTopqSUd1lddFF5eWGh+0KCxeOzz1zfXigFBXDttU5sOnd2UVSdO7tIrAYNqrbTa5lUGUZsGJVgImHUCgE/R48en/H5571Zt658AF8w+fkug0ZAOALLO++UBxSFEui+SkmBvDzXOsnOrthTEw216lj3uq+qTVqai5A6+uiK5UlJ4YfH79rlmmnB+1JSXJdX584VxSOwtGzpnOzBrR1rmRgxYiJh1AqRRoWHkpHhIqWOPbZieVmZq/AuXuxy/U2Z4gb8BadjKi6GE08s327Z0glG27YVX4PXs7LK/SH33utTxzpUHqa7ZInbt2KFGzOyYkX5Mn26m7MjmMaNXV9fwCcSwCKwjBgwkTB8QWBMWqdObmzb+vUumioQpnv55S4l09q1bvnxx4rrn37qksiGkprqnpXlFXGp0DL56CPXqmjZ0rkIqkOt+kwqC9NNS3O+i8MOC39ufv6+4vHoo+GPXb3aDRo85JCKS5cu7rVtW/djhMMc6/USEwnDl4QL0w3XSxNMUZETl1Ah+f5759vYtKlir01xcXmLRsS1Otq0qbi0br1vWbNm7jlaqz6T6obpBpOR4aKljjqqvGzGjPAtk6ZNnRNp+XKYN8/N6xHcXEtNdV1WoSLy7bfuA+/ebY71eoaJhOFLognTDSU11VVwwyWRHTHCiU1KinOsX3KJm55i/XonSOvXV1yWLnWvkbJyBBNomSQnu1ZFdrZb2rRxLaGoGTyYdQMGc8YZ25g1q2nNWieRWiZPPFHxoV5c7Pr5li/fd5k3D7Zvj/weBQVw441OSTt3dl/8AT003whHXERCRE4AblXVX3nbNwHbgExVfaw6ZYYRC+Ec6yecUPk5qu4ZGRCOgJh8/z385z+uMh1cCS8pgV/+suI1mjatKBrh1rOz3RALkVr0m3hCsO73jzJo7YO81P63tPn7zfvW+lNSXFdTly7hr/PTTy7yqk+f8I71rVvh1FPdelIStG9f0YkevGRn79uVZRFYvicuIqGq80RkOICIdAWyVXWMiNwtIjlASTRlqro4HvYadY9oHevBiLiHfNOmLiw3mJKSij6Ta66Bu+92IrJuXfkSvD1/vnuNFMUV9M4VWicPP+y6uJo1c+P5AuvNmlUR4TV4MPf+bzBzJ8DIcz/hyVievYGh9ZEc6+3auSiDYH/IihVuMOGPP1Y8Ni2tYlTWTz+5brFAAkbrwvIlonGalUZEJqnqFZ5YbFbV6SJyAdAa0GjKVHVCmOsOB4YDtG7duue0adNisi8/P5+MjIyYzo0XZmPNqS37/vznI2nevIhzzvmRmTPbsnVrKiNHfl3leaqwa1cyW7emsmWLW7ZuTWXt2nQ++qgFGzemoyqA0qCBUlpaeaLm9PRSmjQppkmTEm9x67NmtaGsbN9h7ikpZbz11vsRfdORaPX223R78EE2FjZjENN4iYtpmbaNJb/9LRsHDgx7TlJREWnr19Nw3TrS160jPWQ9ZefOsOeVpaSwuV8/CrOyKGzZ0i3eelGLFmglEQat3n6bQ55+mrSNGyls1Yrlw4ZFtC+R+O0+6d+//wJV7RVuXyJE4g7gY1V9W0QGAr0BiaZMVf9W2Xv06tVLP/3005jsy6tJuoY4YTbWHD/bF/CbJCc7v8m117oZBrdtc706P/3klnDroWVbt0ZusSQluWiuVq3cErwebsnI8MKIp07l+uvKmJB/KddmvMCT45NqVuP3xoaso81e4WnDBreva1cXdRA6ibuI84G0b+9aMcGvixe7ZlfwB2/UyH2pPmuZ+O1/KCIRRSIRjustQBNvvYm3LVGWGUadJZzfJDnZRV1lZVX/etdcA88849wOxcVuYOO557oor40by5dA+PCOHeGvk54eeO6WP2jH5V/GuCGQfIXzhR90kFsyMyu+NmlSSWix14V1L39iLv0YyZ95khtcl9R337lm108/wZo1TjCCX9escb6S995zKhqJwKj1b7+tOJCmXTungFWNXDefSUJEYg5wBTAD6A4E8i9HW2YYdZJY/CaVsWWLa50EhxHfeGPk4/fscQISKiIbN7phGHPnOh9LcOdDSYkTtspo3LiicATWX129nNKgec/GcT3juJ7UtaV8vxqysoSGzZs7R0xlsc+7djnxyMkJ3zLZtStcJkonEG3a7DsSM7D9+edw992s253JIObw0g8X06Ye+kziFd10MnCSiJwPvAZsEJGhwFZVXeodE1WZYRjRUd0w4vR0OPhgt4QjNIx4+HB48EEXAbZjh1vCrYcr+/FHaNU6iU0bSykprVibLyppsDeMuVGj8pZU8NKyZfB2Y7KyDiOrXU9arFkYvmWybJlTu3CjMdeudS2XvLywrZJ7ebD8egU3uNjpDRuckASW7GynhlVxAA5IjFd00/tAcIzdmDDHRFVmGEZiCNcd1rixW9q2je2aI0Y08ITHBTmdf75LDLl5c8Vl0yb3+v337jV819gnFbYCLRNZpRzXT0hLy/aWXqSlUb50hbQjvfWkYtIKd5C2ext/GNeBElL2uV7ajj2svK0TmWwnnT3sDQ/IzHRiESwewSLyySdw5537pWWyPzMm22A6wzCiora7wyD8yPoLLqj6vKIi150WLCDLlsGL47by7eqDKCGZBpTQtvkeuh2bgYgbGLl9u3uNtBQVpQAtvCU8haSTzXoAUpLLyGxYRNPUAjIb5JO5ZTuZG7eS+fFGMgvW07RsC5msJJPt3nIC47mWDziJ2xnN6ILbSR1xJ6lrtpHavhUp7Vsjbb3BM9FGP02dyr3XlTE3/1JGHloLAQUhmEgYhpEwYhlZD258SGAgYjCrVjXn64mQmlxKSUky51ycUa1BiapOgAoLoXDqy9z6myKmlg4ihSKKSeWMpFkMue4gth91Itu2wfbtSWzfnu4tzdm+Hb7b7nqttouyc2fk2bamcBlTuAx2An8I+mwUkkoRqWwltUEJqcllpKYoaWmQmpZEasMkUhslk9o4hTkfNaJM9w0oSL+yhN1FtfN4N5EwDKPOEK5LrDqIlHdDMeJX7Hp2FSMWT2F4/sNMzLiVdTm5XDo2TN6X8FejtBR27nQtmO/7XMrfN17BB5xMIemkUsgxLOTczPdJv/UGirbspGhrPkXbCijctpuinXso2llE0a4iCneVUrStlKLSJIo8CdlNGkfRmFV0YDtNUZJoxC4uZAYPth5DaPdbrJhIGIZRZ6jtLrHpn3QALgcupxoNnb00aFA+ar/jw2fz8tAfeLc0hXR2U0QqPRt8wV1j28LgRkAj3DjiSti50znd162Ddavg0ksZwZNMZDhp7GYP6RzEDtqsXRCDteGp5rhLwzAMIyYGD2ZDj9O5LmMq8zme6zKmsr7HGdXzHwTmAs7NdZPPd+zIBlpxHeP5kL5cx3jW0zp8lssYsZaEYRhGnAi0TPLyOjC2NkZcjxrF9OGX7832O5Zfe/OQTKz5tT2sJWEYhnGgMniwCwvr2NE5VDp2rPU0JNaSMAzDOJCJdb71KLGWhGEYhhEREwnDMAwjIiYShmEYRkRMJAzDMIyImEgYhmEYEYnbzHTxQEQ2AWEm4o2KLGBzLZqzPzAba47f7QP/2+h3+8D/NvrNvo6q2jLcjjolEjVBRD6NNH2fXzAba47f7QP/2+h3+8D/NvrdvmCsu8kwDMOIiImEYRiGERETiXJqL9nJ/sNsrDl+tw/8b6Pf7QP/2+h3+/ZiPgnDMAwjItaSMAzDMCJiImEYhmFExEQCEJGbRGSoiNyYaFvCISLpInKViJwrIveJiC9/NxHpLCKxTOAVF0Skv4icLiL/EJHmibYnFBFpIyLDReQcEbnBL7+ziJwgIi8Hbfvufgm20a/3S+j36JX5+p4BEwlEpCuQraqTgWYikpNom8JwBlCqqq8D64BjEmtORI4DGifaiHCISEvgMFWdBYxQ1a2JtikMg4FpqjoTWI1PfmdVnQfkg3/vl2Ab8en9EmJjAN/eMwHqvUgAA4CPvfUvgFMSaEsk8oC53no2sDJhlkRARE4H3ky0HZVwBtBJRH4DPCgifrwx5wGjRSQTOApYmmB7wmH3Sy1xANwzgIkEuOHxO7z1fMB33RCquk1Vl3m1uO/9VgsWkTbALlXdnmhbKqEtsEpVHwdeAX6ZYHvC8SmwAXgVKFHV0FqnH7D7pRY4QO4ZwEQCYAvQxFtv4m37Du9P1UNVJyXaljB0B5JFJBdoIyJHJdacsOwG1nrra3A1TL/xa2AsrraeIyI9EmxPOOx+qR0OhHsGMJEAmAP09ta745qqvkJEUoGzVPVfIpIiIt0TbVMwqjpLVfNUNQ9Yr6pfJdqmMHwC9PTW2wBLEmhLJDKAjeoGL80ADk6wPeGw+6UWOEDuGcBEAlVdCmwQkaHAVm/bbwwDTheRKcC7QEmC7dkHcfwKOFJEDk20PaGo6nwAz8YjgNcTa1FYJgFXiMg5QA4+6a8WkZOBk0TkfOA7fHi/hNh4DT68X4JtlHJ8e88EsBHXhmEYRkTqfUvCMAzDiIyJhGEYhhEREwnDMAwjIiYShmEYRkRMJAzDp4jIEBHplGg7jPqNiYRh+BAR6Qf8LdF2GIaJhGH4EFWdCyxLtB2GYSJhGIZhRCQ50QYYxoGAiKQDvwc2Ae2B/wFfAZOBhbikdyXevntVdbV3XjZwG/Ctt+8rVX0l6Lq9gdOB74E+wMOquiborY8Tketw6aQbqerV+/FjGsY+mEgYRnTcD/xTVf8nIoITiUtxqTSGAqeparGXqO0pXGpygGeAq1R1PYCIvCgiX6vqYi8J3Wjg56pa6k2EdAtOVAK0V9U/eOfOEpHDVfXb/f9xDcNh3U2GUQXezGbnqur/ALwEfHOAU71D3lPVYm/fV8ARXmK5nkBRQCA8XgJu8NaHAq+paqm3/U/gkZC3fyVofR3QqpY+lmFEhbUkDKNqsoAUERkUVFaIS5N9UJjjA/MsdME92INZBxzirXfCiQ0Aqro5ClskOpMNo3YwkTCMqtkMlKnqtNAdInJFmOOb4nwXK4EWIfuygBXe+o9Ay5DrNffjJDlG/cW6mwyjClS1DMgTkRMDZSLSS0S6eJuHBJUPBGZ753wCNBGRJkGXOxcY762/DPzS684KENxaMYyEY6nCDSMKvHmnR+K6mAqAZao6w2tJnIWbt6AhcChwh6pu887rCFyHm+SokXferKDr/gI4DfgB15U0RVVXiUgfnD9ivKqOEpHDgWk4h/ktqlq4/z+1YZhIGEaN8ESik6r+JcGmGMZ+wbqbDCNGRKQ1cCbQ389zFBtGTbCWhGEYhhERa0kYhmEYETGRMAzDMCJiImEYhmFExETCMAzDiIiJhGEYhhEREwnDMAwjIv8PY+bH68QiUoAAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots()#创建一个figure \n",
    "ax.plot(history.history['loss'], '-r',marker = \"o\",label='训练集 loss')\n",
    "ax.plot(history.history['val_loss'], '-b',marker = \"*\",label='验证集 loss')\n",
    "\n",
    "####打开网格\n",
    "ax.grid(True)\n",
    "\n",
    "####定义x, y轴的名称\n",
    "ax.set_xlabel('epoch',fontsize=15)\n",
    "ax.set_ylabel('loss',fontsize=15)\n",
    "\n",
    "####定义标题\n",
    "fig.suptitle('实验二：BiLSTM-CRF模型损失函数变化曲线',fontsize=15)\n",
    "\n",
    "####展示图例 legend loc=是用来定义图例的位置的，还有很多选择，大家可以自己尝试\n",
    "ax.legend(loc = 'upper right')\n",
    "\n",
    "plt.savefig(\"实验二：BiLSTM-CRF模型损失函数变化曲线.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
